#include <chrono>
#include <condition_variable>
#include <future>
#include <iostream>
#include <mutex>
#include <queue>
#include <thread>

using namespace std;

void test1() {
  std::thread t([]() { cout << "hello world" << endl; });
  t.join();
}

void test2() {
  int v = 1;
  auto tf = [&](int cv) {
    static mutex mtx;
    // lock_guard<mutex> lock(mtx);
    unique_lock<mutex> lock(mtx);
    v = cv;
  };

  thread t1(tf, 2), t2(tf, 3);
  t1.join();
  t2.join();
  cout << v << endl;
}

void test3() {
  packaged_task<int()> task([]() { return 7; });
  future<int> ret = task.get_future();
  std::thread(std::move(task)).detach();
  ret.wait();
  cout << ret.get() << endl;
}

void test4() {
  queue<int> produced_num;
  mutex mtx;
  condition_variable cv;
  bool notified = false;

  auto producer = [&]() {
    for (int i = 0;; ++i) {
      this_thread::sleep_for(chrono::milliseconds(500));
      unique_lock<mutex> lock(mtx);
      cout << "produce" << i << endl;
      produced_num.push(1);
      notified = true;
      cv.notify_all();
    }
  };

  auto consumer = [&]() {
    while (true) {
      unique_lock<mutex> lock(mtx);
      while (!notified) {
        cv.wait(lock);
      }
      lock.unlock();
      this_thread::sleep_for(chrono::milliseconds(1000));
      lock.lock();

      if (!produced_num.empty()) {
        cout << "consume" << produced_num.front() << endl;
        produced_num.pop();
      }
      notified = false;
    }
  };

  thread p(producer), cs1(consumer), cs2(consumer);
  p.join();
  cs1.join();
  cs2.join();
}

int main() { test4(); }